﻿// <copyright file="HelloGestureTracker.cs" company="Ucaya">
// </copyright>
// <author>Ucaya</author>

namespace KinectExtensions.Helpers
{
    #region

    using System;
    using System.ComponentModel;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Windows.Threading;

    using KinectExtensions.Nui;
    using KinectExtensions.Services;

    #endregion

    /// <summary>
    ///   TODO: Update summary.
    /// </summary>
    public class HelloGestureTracker : INotifyPropertyChanged
    {
        #region Constants and Fields

        private readonly DispatcherTimer recognizingTimer;

        private readonly XyGestureDirection[] validDirections = new[] { XyGestureDirection.E, XyGestureDirection.NE, XyGestureDirection.W, XyGestureDirection.NW, XyGestureDirection.SW, XyGestureDirection.SE };

        private XyGestureDirection? currentXYDirection;

        private JointGestureService gestureService;

        /// <summary>
        ///   The hello gesture count.
        /// </summary>
        private int helloGestureCount;

        private bool isEnabled = true;

        private bool isHandReady;

        private bool isRecognizing;

        /// <summary>
        ///   The last gesture recognized time.
        /// </summary>
        private DateTime lastGestureRecognizedTime;

        private int sameSideGestureCount;

        private int sameSideGestureNeeded = 4;

        #endregion

        #region Constructors and Destructors

        /// <summary>
        ///   The last hello gesture.
        /// </summary>
        //  private SupportedGesture? lastHelloGesture;
        public HelloGestureTracker()
        {
            this.recognizingTimer = new DispatcherTimer();
            this.recognizingTimer.Interval = TimeSpan.FromSeconds(1);
            this.recognizingTimer.Tick += delegate
                {
                    this.recognizingTimer.Stop();
                    this.IsRecognizing = false;
                    this.helloGestureCount = 0;
                    this.CurrentXYDirection = null;
                };
        }

        #endregion

        #region Public Events

        public event EventHandler HelloGestureDetected;

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

        #region Public Properties

        public XyGestureDirection? CurrentXYDirection
        {
            get
            {
                return this.currentXYDirection;
            }
            set
            {
                if (this.currentXYDirection != value)
                {
                    this.currentXYDirection = value;
                    this.RaisePropertyChanged(n => n.CurrentXYDirection);
                }
            }
        }

        public JointGestureService GestureService
        {
            get
            {
                return this.gestureService;
            }
            set
            {
                if (this.GestureService != null)
                {
                    this.GestureService.GestureSegmentDetected -= this.GestureService_GestureSegmentDetected;
                }

                this.gestureService = value;

                if (this.GestureService != null)
                {
                    this.GestureService.GestureSegmentDetected += this.GestureService_GestureSegmentDetected;
                }

                this.RaisePropertyChanged(n => n.GestureService);
            }
        }

        public bool IsEnabled
        {
            get
            {
                return this.isEnabled;
            }
            set
            {
                if (this.isEnabled != value)
                {
                    this.isEnabled = value;
                    this.RaisePropertyChanged(n => n.IsEnabled);
                }
            }
        }

        public bool IsHandReady
        {
            get
            {
                return this.isHandReady;
            }
            set
            {
                if (this.isHandReady != value)
                {
                    this.isHandReady = value;
                    this.RaisePropertyChanged(n => n.IsHandReady);
                }
            }
        }

        public bool IsRecognizing
        {
            get
            {
                return this.isRecognizing;
            }
            set
            {
                if (this.isRecognizing != value)
                {
                    this.isRecognizing = value;
                    this.RaisePropertyChanged(n => n.IsRecognizing);
                }
            }
        }

        public int SameSideGestureCount
        {
            get
            {
                return this.sameSideGestureCount;
            }
            set
            {
                this.sameSideGestureCount = value;
                this.RaisePropertyChanged(n => n.SameSideGestureCount);
            }
        }

        public int SameSideGestureNeeded
        {
            get
            {
                return this.sameSideGestureNeeded;
            }
            set
            {
                this.sameSideGestureNeeded = value;
                this.RaisePropertyChanged(n => n.SameSideGestureNeeded);
            }
        }

        #endregion

        #region Methods

        protected virtual void OnHelloGestureDetected()
        {
            if (this.HelloGestureDetected != null)
            {
                this.HelloGestureDetected(this, EventArgs.Empty);
            }
        }

        protected void RaisePropertyChanged<TProperty>(Expression<Func<HelloGestureTracker, TProperty>> expression)
        {
            if (this.PropertyChanged != null)
            {
                this.PropertyChanged(this, new PropertyChangedEventArgs(this.GetPropertyName(expression)));
            }
        }

        private void GestureService_GestureSegmentDetected(object sender, GestureSegmentEventArgs e)
        {
            if (!this.IsEnabled || !this.validDirections.Contains(e.GestureDirectionXY) || e.Duration.TotalSeconds > 0.12)
            {
                return;
            }

            DateTime dt = DateTime.Now;
            if (this.CurrentXYDirection == null || (dt - this.lastGestureRecognizedTime).TotalMilliseconds > 1000)
            {
                this.CurrentXYDirection = e.GestureDirectionXY;
                this.SameSideGestureCount = 1;
            }
            else if (this.CurrentXYDirection.HasValue)
            {
                if (this.IsSameGestureDirection(this.CurrentXYDirection.Value, e.GestureDirectionXY))
                {
                    this.SameSideGestureCount++;
                }
                else if (this.SameSideGestureCount >= this.SameSideGestureNeeded)
                {
                    this.CurrentXYDirection = e.GestureDirectionXY;
                    this.SameSideGestureCount = 1;
                    this.helloGestureCount++;
                    if (this.helloGestureCount == 3)
                    {
                        this.helloGestureCount = 0;
                        this.CurrentXYDirection = null;
                        this.OnHelloGestureDetected();
                    }
                }
            }
            this.lastGestureRecognizedTime = dt;
            this.SetIsRecognizing();
        }

        private bool IsSameGestureDirection(XyGestureDirection dir1, XyGestureDirection dir2)
        {
            if (dir1 == XyGestureDirection.E || dir1 == XyGestureDirection.NE || dir1 == XyGestureDirection.SE)
            {
                return dir2 == XyGestureDirection.E || dir2 == XyGestureDirection.NE || dir2 == XyGestureDirection.SE;
            }

            if (dir1 == XyGestureDirection.W || dir1 == XyGestureDirection.NW || dir1 == XyGestureDirection.SW)
            {
                return dir2 == XyGestureDirection.W || dir2 == XyGestureDirection.NW || dir2 == XyGestureDirection.SW;
            }

            return false;
        }

        private void SetIsRecognizing()
        {
            if (this.recognizingTimer.IsEnabled)
            {
                this.recognizingTimer.Stop();
            }
            this.recognizingTimer.Start();
            this.IsRecognizing = true;
        }

        #endregion
    }
}